/**
Contains implementation for UDP Client 
*/

#include "globals.h"

uint32_t seq=0;

int main ()
{

	char ip_client [INET_ADDRSTRLEN]; 
	char ip_server [INET_ADDRSTRLEN];
	char filename [TMP_BUFSIZE]="\0"; 
	char buf [TMP_BUFSIZE]="\0";
	char * ptr;
	
	int server_port;
	
	int win;
	int seed;
	int mu;
	int i=1;
	int j;
	int sockfd;
	int flags;
	
	float p;
	
	FILE * fp;
	
	struct ifi_info	*ifi;
	struct ifi_info *ifihead;	

	struct sockaddr * sa;
	struct sockaddr_in cliaddr;
	struct sockaddr_in servaddr;
	struct sockaddr_in sock_nm;	
	
	socklen_t len;	
    
    	char ip_addr[INET_ADDRSTRLEN];
    	char net_msk[INET_ADDRSTRLEN];
    	char sub_msk[INET_ADDRSTRLEN];
    	char check_sub_msk[INET_ADDRSTRLEN];
    	
    	struct packet pk;
	
	srand(time(NULL));
	seq = rand()%100000;
	
	
    parse_client_in(ip_server, &server_port, filename, &win, &seed, &p, &mu);

	for ( ifihead = ifi = Get_ifi_info_plus( AF_INET, 1 ), i = 0; ifi != NULL; ifi = ifi->ifi_next, i++) 
	{
        bzero(ip_addr, INET_ADDRSTRLEN);
        bzero(net_msk, INET_ADDRSTRLEN);
        bzero(sub_msk, INET_ADDRSTRLEN);

	    sa = ifi->ifi_addr;
    	strcpy( ip_addr, Sock_ntop_host( sa, sizeof( *sa ) ) );
		sa =  ifi->ifi_ntmaddr;
		strcpy( net_msk, Sock_ntop_host( sa, sizeof( *sa ) ) );

        get_subnet_mask(ip_addr, net_msk, sub_msk);

		printf( "%s Client Interface %d:\n\tIP Address :: %s\n\tNetwork Mask :: %s\n\tSubnet Mask :: %s\n\n", 
                CLI_LOG, (i+1), ip_addr, net_msk, sub_msk );

        flags = 0;

		if( strcmp( ip_server, ip_addr ) == 0 )
		{
            //
            // Client and Server on the same host		
            //			
			
            strcpy( ip_server, "127.0.0.1" );
			strcpy( ip_client, ip_addr );
			printf( "%s Client and Server are on the same host\n\tIPclient :: %s IPserver :: %s \n", 
                    CLI_LOG, ip_client, ip_server );
            
            //
            // If Client and Server are on the same host then flags 
            // is set to MSG_DONTROUTE in send function
            //	
		
            flags = MSG_DONTROUTE;
			break;
		}
		else 
		{
            //
            // Check if Server is local to Client i.e. if 
            // Client and Server share the same subnet mask
            //

			bzero(check_sub_msk, INET_ADDRSTRLEN);
            get_subnet_mask(ip_addr, net_msk, check_sub_msk);
		
			if( strcmp( check_sub_msk, sub_msk ) == 0 )
			{
				strcpy( ip_client, ip_addr );
				printf("%s Client and Server are local i.e. in the same subnet\n\tIPclient :: %s IPserver :: %s\n", 
                        CLI_LOG, ip_client, ip_server);
				break;
			}
		}

		if(ifi->ifi_next == NULL )		
		{
            //
            // Client and Server are neither local nor on the same host
            //		
			
            strcpy( ip_client, ip_addr );
			printf( "%s Client and Server are not local\n\tIPclient :: %s IPserver :: %s \n", 
                    CLI_LOG, ip_client, ip_server );
		}
	}

	free_ifi_info_plus(ifihead);

    //
    // Creating a UDP socket and Binding with the Client's IP 
    // and a ephemeral port chosen by the Kernel
    //

	sockfd = Socket( AF_INET, SOCK_DGRAM, 0 );
	bzero( &cliaddr, sizeof( cliaddr ) );
	cliaddr.sin_family = AF_INET;
	inet_pton( AF_INET, ip_client, &cliaddr.sin_addr );
	cliaddr.sin_port = htons( 0 );
	
    Bind( sockfd, ( SA * ) &cliaddr, sizeof( cliaddr ) );
	
	len = sizeof( sock_nm );
	getsockname( sockfd, ( SA * ) &sock_nm, &len );

	printf("%s Client Socket created and Binded\n\tIPClient:Portno : %s \n", 
            CLI_LOG, Sock_ntop( ( SA * ) &sock_nm, len ) );

    //
    // Connecting the socket by specifying Server's IP 
    // and well known port using UDP Connect
    //
	
	bzero( &servaddr, sizeof( servaddr ) );
	servaddr.sin_family = AF_INET;
	inet_pton( AF_INET, ip_server, &servaddr.sin_addr );
	servaddr.sin_port = htons( server_port );
	
    	Connect( sockfd, ( SA * ) &servaddr, sizeof( servaddr ) );
	
	bzero( &sock_nm, sizeof( sock_nm ) );
	len = sizeof( sock_nm );
	getpeername( sockfd, ( SA * ) &sock_nm, &len );
	
    	printf("%s After UDP Connect \n\tIPServer:Portno : %s \n", 
            CLI_LOG, Sock_ntop( ( SA * ) &sock_nm, len ) );
	
	pk = get_packet(pk, filename, win);
	pk.ackno = -1;
    	send( sockfd, &pk, sizeof(pk), flags );
	printf( "%s Packet sent to Server [ SEQ: %d , ACK: %d ] :: Request for file '%s' \n", CLI_LOG, pk.seqno , pk.ackno, filename );

	//
	// Client Receives a new Port number from Server
	//


	recv( sockfd, &pk, sizeof(pk), flags);
	printf( "%s Packet recieved from Server [ SEQ: %d , ACK: %d ] :: Data :- %s\n", CLI_LOG, pk.seqno , pk.ackno, pk.data);	
    	pk = get_packet(pk, "", win);
    	send( sockfd, &pk, sizeof(pk), flags );
	printf( "%s Packet sent to Server [ SEQ: %d , ACK: %d ]\n", CLI_LOG, pk.seqno , pk.ackno);

		
   /* 	bzero(buf, TMP_BUFSIZE);

	recv( sockfd, buf, TMP_BUFSIZE, flags);
	ptr=strchr( buf, ':' );
	strcpy( buf, ( ptr+1 ) );

    //
    // Client Reconnects to Server on the new Server port (the ephemeral port)
    //

	servaddr.sin_port = htons( atoi( buf ) );
	
    Connect( sockfd, (SA*) &servaddr, sizeof(servaddr) );
	
    printf("%s New connection established with Server at Port number: %s\n", 
            CLI_LOG, buf);
	
    strcpy( buf, "ACK" );
	send( sockfd, buf, TMP_BUFSIZE, flags );
	printf("%s ACK sent to Server\n", CLI_LOG);
	
	while(TRUE)
	{
        //
        // File Transfer operation
        //	

		recv( sockfd, buf, TMP_BUFSIZE, flags );
		
		if( buf[0] == EOF )
		{filename, 
			break;
		}	
		else
		{
			printf("%s Read buffer: %s", CLI_LOG, buf);	
		}	
		
	}
	
	close( sockfd );
	*/
	return( 0 );
}

/**
TODO: Description
*/
void
parse_client_in(
    char ip_server[],
    int * server_port,
    char filename[],
    int * win,
    int * seed,
    float * p,
    int * mu
    )
{
    
    FILE* fp;
	char buf [TMP_BUFSIZE] = "\0";

    int flags;
    int i ;
    int j;
	
    struct in_addr ipv4_addr;

    if ((fp = fopen("client.in","r")) == NULL)
	{
		printf( "%s client.in not found\n", CLI_LOG);
		exit( 1 );
	}
	else
	{
        i = 1;

		while ( Fgets ( buf, TMP_BUFSIZE, fp ) != NULL )
		{
			flags=0;

			if( i != 1 && i != 3 )
			{
				for( j = 0; buf[j] != '\0'; j++ )
				{
					if( isdigit ( buf[j] ) )
					{
						continue;
					}	
					else if( flags == -1 )
					{
						break;
					}	
					else if( i==6 && buf[j] == '.' )
					{
						flags = -1;					
					}
					else 
					{
						break;	
					}
				}

                if( j != strlen( buf )-1 )
                {
					flags = 1;
                }
			}

			switch(i)
			{
				case 1: 
					strcpy( ip_server, buf );
					ip_server[ strlen(buf) - 1 ] = '\0';
				
                    if( inet_pton ( AF_INET, ip_server, &ipv4_addr.s_addr ) <= 0 )
					{
						printf( "%s Error client.in :: Line 1 :: contains invalid Server IP\n", CLI_LOG);
						exit( 1 );
					}	
					
                    i++;
					break;
				case 2:
					if( flags || ( *server_port = atoi(buf) ) == 0 )
					{
						printf( "%s Error client.in :: Line 2 :: Invalid Port number\n", CLI_LOG );
						exit( 1 );
					}	
					
                    i++;
					break;
				case 3:
					strcpy( filename, buf );
					filename[ strlen(buf)-1 ] = '\0';
					i++;
					break;	
				case 4:
					if( flags || ( *win=atoi(buf) ) == 0 )
					{
						printf( "%s Error client.in :: Line 4 :: Invalid Receiving Sliding Window Size\n", CLI_LOG );
						exit( 1 );
					}	
					
                    i++;
					break;	
				case 5:
					if( flags || ( *seed = atoi(buf) ) == 0 )
					{
						printf( "%s Error client.in :: Line 5 :: Invalid Seed value\n", CLI_LOG );
						exit( 1 );
					}	
					
                    i++;					
					break;	
				case 6:
					*p=atof( buf );
					
                    if( flags == 1 || *p < 0.0 || *p > 1.0 )
					{
						printf( "%s Error client.in :: Line 6 :: Invalid Probability value\n", CLI_LOG );
						exit( 1 );
					}	
					
                    i++;									
					break;	
				case 7:
					if( flags || ( *mu = atoi(buf) ) == 0 )
					{
						printf( "%s Error client.in :: Line 7 :: Invalid Mean value\n", CLI_LOG );
						exit( 1 );
					}	
					
                    i++;								
					break;	
			}

            bzero(buf, TMP_BUFSIZE);            
		}
		
		fclose(fp);
	}
}


struct packet
get_packet(
	struct packet p,
	char buf[TMP_BUFSIZE], 
	uint32_t  win)
{
	p.ackno = p.seqno+1;
	p.win = win;
	p.seqno = seq;
	strcpy(p.data, buf);
	seq++;
	return p;
}
